/*
 * Copyright (c) 2009, Luke Emrose
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the
 * distribution.  Neither the name of Luke Emrose Sole Trader nor the
 * names of its contributors may be used to endorse or promote
 * products derived from this software without specific prior written
 * permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 
 /**
 * @file
 * code by Luke Emrose - evolutionarytheory@gmail.com
 *
 * print_ptc.cpp
 * 
 * Print out the contents of a specified 3delight point cloud.
 */

#include <iostream>
#include <vector>
#include <string>
#include <pointcloud.h>
#include <ri.h>

/**
 * @brief
 * utility class to facilitate printing of point cloud user variables
 */
struct ptc_vars_printer
{
	/**
	 * @brief
	 * constructor
	 * takes info about the user variables ready for printing
	 */
	ptc_vars_printer
	(
		int in_num_vars,
		char** in_var_names,
		char** in_var_types
	)
		:
		var_data_size( in_num_vars ),
		var_names( in_num_vars )
	{
		for( int i = 0; i < in_num_vars; ++i )
		{
			var_names[ i ] = in_var_names[ i ];
			std::string curr_type( in_var_types[ i ] );

			// gather the appropriate sizes for different user variable types
			if( curr_type == "float" )
			{
				var_data_size[ i ] = 1;
			}
			else if( curr_type == "point" )
			{
				var_data_size[ i ] = 3;
			}
			else if( curr_type == "vector" )
			{
				var_data_size[ i ] = 3;
			}
			else if( curr_type == "normal" )
			{
				var_data_size[ i ] = 3;
			}
			else if( curr_type == "color" )
			{
				var_data_size[ i ] = 3;
			}
			else if( curr_type == "matrix" )
			{
				var_data_size[ i ] = 16;
			}
		}
	}

	/**
	 * @brief
	 * function to take a user data array, and print it nicely
	 */
	void get_user_vars_string( std::ostream& out, const std::vector< RtFloat >& data )
	{
		size_t data_index = 0;

		// iterate through each user variable
		for( size_t curr_var_id = 0; curr_var_id < var_data_size.size(); ++curr_var_id )
		{
			// iterate through the components of each user variable
			for( RtInt i = 0; i < var_data_size[ curr_var_id ]; ++i )
			{
				out << ", " << var_names[ curr_var_id ] << " = " << data[ data_index++ ];
			}
		}
	}

private:
	std::vector< RtInt > var_data_size;
	std::vector< std::string > var_names;
};

/**
 * @brief
 * print out the contents of a specified 3delight point cloud
 */
int main( int argc, char* argv[] )
{
	// pointer to the point cloud file
	PtcPointCloud ptc_file;

	// user information
	std::cout << "Outputting point cloud: " << argv[ 1 ] << std::endl;
	std::cout << std::endl;
	
	// load the point cloud
	//ptc_file = PtcSafeOpenPointCloudFile( argv[ 1 ] );
	ptc_file = PtcOpenPointCloudFile( argv[ 1 ], 0, 0, 0 );

	// print out all variables data
	RtInt success;

	// number of points in the point cloud
	RtInt num_points;
	success = PtcGetPointCloudInfo( ptc_file, "npoints", &num_points );
	std::cout << "number of points = " << num_points << std::endl;

	// number of variables in the point cloud
	RtInt num_vars;
	success = PtcGetPointCloudInfo( ptc_file, "nvars", &num_vars );
	std::cout << "number of user variables = " << num_vars << std::endl;

	// size of the data required to hold the resulting point variables
	RtInt data_size;
	success = PtcGetPointCloudInfo( ptc_file, "datasize", &data_size );
	std::cout << "number of floats required to store all user variables = " << data_size << std::endl << std::endl;

	// allocate memory for variables information
	char** var_names = new char*[ num_vars ];
	char** var_types = new char*[ num_vars ];

	// read in the names and types of the variables
	success = PtcGetPointCloudInfo( ptc_file, "varnames", var_names );
	success = PtcGetPointCloudInfo( ptc_file, "vartypes", var_types );

	std::cout << "user variables:" << std::endl;

	// print out the variables
	for( RtInt i = 0; i < num_vars; ++i )
	{
		std::cout << "\tname: \"" << var_names[ i ] << "\", type: \"" << var_types[ i ] << "\"" << std::endl;
	}

	std::cout << std::endl << "points:" << std::endl << std::endl;

	// print all points within the file
	RtFloat p[ 3 ];
	RtFloat n[ 3 ];
	RtFloat r;
	std::vector< RtFloat > data( data_size );
	std::vector< RtFloat >::const_iterator data_iter;

	// prepare a user variables printer object
	ptc_vars_printer vars_printer( num_vars, var_names, var_types );

	// print each point one by one
	for( int i = 0; i < num_points; ++i )
	{
		success = PtcReadDataPoint( ptc_file, p, n, &r, &( data[ 0 ] ) );
		if( success )
		{
			std::cout << "point: " << i + 1
				<< ", p = ( " << p[ 0 ] << ", " << p[ 1 ] << ", " << p[ 2 ] << " )"
				<< ", n = ( " << n[ 0 ] << ", " << n[ 1 ] << ", " << n[ 2 ] << " )"
				<< ", r = " << r;

			// print the user variable data
			vars_printer.get_user_vars_string( std::cout, data );

			std::cout << std::endl;
		}
	}

	// deallocate memory for variables information
	delete [] var_names;
	delete [] var_types;

	// close the point cloud file, and release any allocated memory
	PtcClosePointCloudFile( ptc_file );
}